package cn.zy.amyan.core.util;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Logger;

/**
 * @author 子言
 * @date 2024/5/1 下午 15:15
 */
public class ClassUtil {
	private static final Logger log = Logger.getLogger("appLogger");
	public static ArrayList<Class> getAllClassByInterface(Class clazz) {
		ArrayList<Class> list = new ArrayList<>();
		if (clazz.isInterface()) {
			try {
				ArrayList<Class> allClass = getAllClass(clazz.getPackage().getName());
				for (int i = 0; i < allClass.size(); i++) {
					/**
					 * 判断是不是同⼀个接⼝
					 */
					// isAssignableFrom:判定此 Class 对象所表示的类或接⼝与指定的 Class
					// 参数所表示的类或接⼝是否相同，或是否是其超类或超接⼝
					if (clazz.isAssignableFrom(allClass.get(i))) {
						if (!clazz.equals(allClass.get(i))) {
							// ⾃身并不加进去
							list.add(allClass.get(i));
						}
					}
				}
			} catch (Exception e) {
				log.severe("出现异常：" + e.getMessage());
			}
		}
		log.info("class list size: " + list.size());
		return list;
	}

	private static ArrayList<Class> getAllClass(String packageName) {
		log.info("packageName to search: " + packageName);
		List<String> classNameList = getClassName(packageName);
		ArrayList<Class> list = new ArrayList<>();
		classNameList.forEach(className -> {
			try {
				list.add(Class.forName(className));
			} catch (ClassNotFoundException e) {
				log.severe("load class by name failed: " + className);
				log.severe("error message: " + e.getMessage());
			}
		});
		log.info("find list size: " + list.size());
		return list;
	}

	public static List<String> getClassName(String packageName) {
		List<String> fileNames = null;
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		String packagePath = packageName.replace(".", "/");
		URL url = loader.getResource(packagePath);
		if (null != url) {
			String type = url.getProtocol();
			log.fine("file type:" + type);
			if ("file".equals(type)) {
				String fileSearchPath = url.getPath();
				log.fine("fileSearchPath: " + fileSearchPath);
				fileSearchPath = fileSearchPath.substring(0, fileSearchPath.indexOf("/classes"));
				log.fine("fileSearchPath: " + fileSearchPath);
				fileNames = getClassNameByFile(fileSearchPath);
			} else if ("jar".equals(type)) {
				try {
					JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
					JarFile jarFile = jarURLConnection.getJarFile();
					fileNames = getClassNameByJar(jarFile, packagePath);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} else {
			throw new RuntimeException("file system no support! cannot load MsgProcessor!");
		}
		return fileNames;
	}

	private static List<String> getClassNameByFile(String filePath) {
		List<String> myClassName = new ArrayList<>();
		File file = new File(filePath);
		File[] childFiles = file.listFiles();
		for (File childFile : childFiles) {
			if (childFile.isDirectory()) {
				myClassName.addAll(getClassNameByFile(childFile.getPath()));
			} else {
				String childFilePath = childFile.getPath();
				if (childFilePath.endsWith(".class")) {
					childFilePath = childFilePath.substring(childFilePath.indexOf("\\classes") + 9, childFilePath.lastIndexOf("."));
					childFilePath = childFilePath.replace("\\", ".");
					myClassName.add(childFilePath);
				}
			}
		}
		return myClassName;
	}

	private static List<String> getClassNameByJar(JarFile jarFile, String packagePath) {
		List<String> myClassName = new ArrayList<>();
		try {

			Enumeration<JarEntry> entries = jarFile.entries();
			while (entries.hasMoreElements()) {
				JarEntry jarEntry = entries.nextElement();
				String entryName = jarEntry.getName();
				if (entryName.endsWith(".class")) {
					entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
					myClassName.add(entryName);
					log.fine("Find Class：" + entryName);
				}
			}
		} catch (Exception e) {
			log.severe("发生异常：" + e.getMessage());
		}
		return myClassName;
	}

}
